/*
 * \brief  Service management framework
 * \author Norman Feske
 * \date   2006-07-12
 */

/*
 * Copyright (C) 2006-2009 Norman Feske
 * Genode Labs, Feske & Helmuth Systementwicklung GbR
 *
 * This file is part of the Genode OS framework, which is distributed
 * under the terms of the GNU General Public License version 2.
 */

#ifndef _INCLUDE__BASE__SERVICE_H_
#define _INCLUDE__BASE__SERVICE_H_

#include <root/client.h>
#include <base/lock.h>
#include <base/printf.h>
#include <util/list.h>

namespace Genode {

	/**
	 * A client is someone who applies for a service. If the
	 * service is not available yet, we enqueue the client
	 * into a wait queue and wake him up as soon as the
	 * requested service gets available.
	 */
	class Client : public List<Client>::Element
	{
		private:

			Cancelable_lock _service_apply_lock;
			const char     *_apply_for;

		public:

			/**
			 * Constructor
			 */
			Client(): _service_apply_lock(Lock::LOCKED), _apply_for(0) { }

			virtual ~Client() { }

			/**
			 * Return name of client
			 *
			 * This function is here for debugging purposes.
			 */
			virtual const char *name() { return "<noname>"; }

			/**
			 * Set/Request service name that we are currently applying for
			 */
			void apply_for(const char *apply_for) { _apply_for = apply_for; }
			const char *apply_for() { return _apply_for; }

			/**
			 * Service wait queue support
			 */
			void sleep()  { _service_apply_lock.lock(); }
			void wakeup() { _service_apply_lock.unlock(); }
	};


	class Service
	{
		private:

			enum { _NAME_LEN = 32 };
			char _name[_NAME_LEN];

		public:

			virtual ~Service() { }

			Service(const char *name) { strncpy(_name, name, sizeof(_name)); }

			virtual Root *root() = 0;

			const char *name() { return _name; }
	};


	/**
	 * Representation of a service that is implemented locally
	 */
	class Local_service : public Service, public List<Local_service>::Element
	{
		private:

			Root *_root;

		public:

			Local_service(const char *name, Root *root)
			: Service(name), _root(root) { }

			Root *root() { return _root; }
	};


	/**
	 * Class definition in include/base/child.h
	 */
	class Child;


	/**
	 * Representation of a service that is implemented in a child
	 */
	class Remote_service : public Service, public List<Remote_service>::Element
	{

		private:

			Root_client _root_client;
			Child      *_server;

		public:

			/**
			 * Constructor
			 *
			 * \param name   name of service
			 * \param root   capability to root interface
			 * \param server child that provides the service
			 */
			Remote_service(const char *name, Root_capability root, Child *server)
			: Service(name), _root_client(root), _server(server) { }

			Root  *root()   { return &_root_client; }
			Child *server() { return _server; }
	};


	/**
	 * Container for holding service representations
	 */
	template <typename ST>
	class Service_pool
	{
		protected:

			Lock         _service_wait_queue_lock;
			List<Client> _service_wait_queue;
			List<ST>     _services;

		public:

			/**
			 * Probe service with specified name
			 */
			ST *find(const char *name)
			{
				Lock::Guard lock_guard(_service_wait_queue_lock);

				for (ST *s = _services.first(); s; s = s->next())
					if (strcmp(s->name(), name) == 0) return s;

				return 0;
			}

			void dump_service_wait_queue()
			{
				printf("Service wait queue:\n");
				Lock::Guard lock_guard(_service_wait_queue_lock);
				for (Client *c = _service_wait_queue.first(); c; c = c->next())
					printf("  %s waits for service %s\n", c->name(), c->apply_for());
			}

			/**
			 * Wait for service
			 *
			 * This function is called by the clients's thread
			 * when requesting a session creation. It blocks
			 * if the requested service is not available.
			 *
			 * \return  service structure that matches the request or
			 *          0 if the waiting was canceled.
			 */
			ST *wait_for_service(const char *name, Client *client)
			{
				ST *service;

				client->apply_for(name);

				_service_wait_queue_lock.lock();
				_service_wait_queue.insert(client);
				_service_wait_queue_lock.unlock();

				do {
					service = find(name);

					/*
					 * The service that we are seeking is not available today.
					 * Lets sleep a night over it.
					 */
					if (!service) {
						printf("%s: service %s not yet available - sleeping\n",
						     client->name(), name);

						try {
							client->sleep();
							printf("%s: service %s got available\n", client->name(), name);
						} catch (Blocking_canceled) {
							printf("%s: cancel waiting for service\n", client->name());
							break;
						}
					}

				} while (!service);

				/* we got what we needed, stop applying */
				_service_wait_queue_lock.lock();
				_service_wait_queue.remove(client);
				_service_wait_queue_lock.unlock();

				client->apply_for(0);

				return service;
			}

			/**
			 * Register service
			 *
			 * This function is called by the server's thread.
			 */
			void insert(ST *service)
			{
				/* make new service known */
				_services.insert(service);

				/* wake up applicants waiting for the service */
				Lock::Guard lock_guard(_service_wait_queue_lock);
				for (Client *c = _service_wait_queue.first(); c; c = c->next())
					if (strcmp(service->name(), c->apply_for()) == 0)
						c->wakeup();
			}

			/**
			 * Unregister service
			 */
			void remove(ST *service) { _services.remove(service); }
	};


	typedef Service_pool<Local_service>  Local_service_pool;


	class Remote_service_pool : public Service_pool<Remote_service>
	{
		public:

			/**
			 * Return first service provided by specified server
			 */
			Remote_service *find_by_server(Child *server)
			{
				Lock::Guard lock_guard(_service_wait_queue_lock);

				for (Remote_service *s = _services.first(); s; s = s->next())
					if (s->server() == server) return s;

				return 0;
			}
	};
}

#endif /* _INCLUDE__BASE__SERVICE_H_ */
